#pragma once

#include <iostream>
#include <vector>
#include <string>
#include <pthread.h>
#include <semaphore.h>

template<class T>
class Circular_Queue
{
public:
    Circular_Queue(const int& cap = 5)
    :_capacity(cap)
    ,_queue(cap)
    {
        //初始化信号量，析构时记得销毁
        sem_init(&_SpaceSem, 0, _capacity);
        sem_init(&_DataSem, 0, 0);

        _ProducerStep = _ConsumerStep = 0;

        //初始化锁，析构时记得销毁
        pthread_mutex_init(&_pmutex, nullptr);
        pthread_mutex_init(&_cmutex, nullptr);
    }
    
    void push(const T& in)
    {
        // 先申请信号量，再加锁，这样可以提前知道有没有资源进行生产，不用加锁阻塞再判断有没有资源
        //申请信号量
        int n = sem_wait(&_SpaceSem); //进行生产，所以申请成功是对空间进行减1
        if(n != 0)
            std::cout << "sem_wait error" << std::endl;

        // 申请信号量成功, 意味着，我一定能进行正常的生产
        pthread_mutex_lock(&_pmutex);
        _queue[_ProducerStep++] = in;
        _ProducerStep %= _capacity;
        pthread_mutex_unlock(&_pmutex);

        int m = sem_post(&_DataSem); //生产成功，供消费者消费的数据增加
        if(m != 0)
            std::cout << "sem_post error" << std::endl;
    }

    void pop(T* out)
    {
        //申请信号量
        int n = sem_wait(&_DataSem);
        if(n != 0)
            std::cout << "sem_wait error" << std::endl;

        // 申请信号量成功
        pthread_mutex_lock(&_cmutex);
        *out = _queue[_ConsumerStep++];
        _ConsumerStep %= _capacity;
        pthread_mutex_unlock(&_cmutex);

        int m = sem_post(&_SpaceSem);
        if(m != 0)
            std::cout << "sem_post error" << std::endl;
    }

    ~Circular_Queue()
    {
        sem_destroy(&_SpaceSem);
        sem_destroy(&_DataSem);

        pthread_mutex_destroy(&_pmutex);
        pthread_mutex_destroy(&_cmutex);
    }
private:
    std::vector<T> _queue;
    int _capacity;

    sem_t _SpaceSem; // 生产者 想生产，看中的是空间资源
    sem_t _DataSem;  // 消费者 想消费，看中的是数据资源

    int _ProducerStep;
    int _ConsumerStep;

    pthread_mutex_t _pmutex;
    pthread_mutex_t _cmutex;
};